package com.example.encryptedsharedpreferences

import android.content.SharedPreferences
import android.util.Base64
import androidx.collection.ArraySet
import java.nio.ByteBuffer
import java.nio.charset.Charset

class EncryptedSharedPreferences constructor(
    private val mSharedPreferences: SharedPreferences,
    private val mEncryption: IEncryption
) : SharedPreferences, SharedPreferences.Editor {

    private val mEditor = mSharedPreferences.edit()

    companion object {

        private const val NULL_VALUE = "_NULL_"
        private val UTF_8 = Charset.forName("UTF-8")

        //一个 long 类型锁占的字节数
        private const val Long_BYTES = java.lang.Long.SIZE / java.lang.Byte.SIZE

        //一个int 类型所占的字节数
        private const val Integer_BYTES = Integer.SIZE / java.lang.Byte.SIZE

        //一个float 类型所占的字节数
        private const val Float_BYTES = java.lang.Float.SIZE / java.lang.Byte.SIZE

        //一个byte 类型所占的字节数
        private const val Byte_BYTES = 1;

    }

    override fun getAll(): MutableMap<String, *> {
        val returnValue: MutableMap<String, Any?> = HashMap()
        val all = mSharedPreferences.all
        for (tmp in all) {
            var key = tmp.key
            val keyRel = String(decryptBytes(key))
            val value = getDecryptedData(keyRel)
            returnValue[keyRel] = value
        }

        return returnValue
    }

    override fun getString(key: String?, defValue: String?): String? {
        val value = getDecryptedData(key)
        return if (value != null && value is String) value else defValue
    }

    //解密过程
    private fun getDecryptedData(key: String?): Any? {
        val safeKey = key ?: NULL_VALUE
        var returnValue: Any? = null
        try {
            //键的加密：这里使用加密之后的键去取加密的值
            val encryptedKey = encryptString(safeKey)
            //读取存入sp中的数据
            val encryptedValue = mSharedPreferences.getString(encryptedKey, null)
            if (null != encryptedValue) {
                //还原
                val value = decryptBytes(encryptedValue)
                val buffer = ByteBuffer.wrap(value)
                buffer.position(0)
                //读取第一个int位
                val typeId = buffer.int
                when (DataType.fromType(typeId)) {
                    DataType.STRING -> {
                        val stringLength = buffer.int
                        val byteArray = ByteArray(stringLength)
                        buffer.get(byteArray)
                        val stringValue = String(byteArray, UTF_8)
                        returnValue = if (stringValue == NULL_VALUE) {
                            null
                        } else {
                            stringValue
                        }
                    }
                    DataType.STRING_SET -> {
                        //StringSet 的数据结构：数据类型 + 多个String 结构
                        val stringSet = ArraySet<String>()
                        while (buffer.hasRemaining()) {

                            val subStringLength = buffer.int
                            val byteArray = ByteArray(subStringLength)
                            buffer.get(byteArray)
                            stringSet.add(String(byteArray, UTF_8))
                            returnValue =
                                if (stringSet.size == 1 && NULL_VALUE == stringSet.valueAt(0)) {
                                    null
                                } else {
                                    stringSet
                                }
                        }
                    }
                    DataType.INT -> returnValue = buffer.int
                    DataType.LONG -> returnValue = buffer.long
                    DataType.FLOAT -> returnValue = buffer.float
                    DataType.BOOLEAN -> returnValue = buffer.get() != 0.toByte()
                }
            }
        } catch (ex: Throwable) {
            throw SecurityException("Could not decrypt value. " + ex.message, ex)
        }

        return returnValue
    }

    //还原
    private fun decryptBytes(encryptedStr: String): ByteArray {

        return try {
            val encodeBytes = encryptedStr.toByteArray(UTF_8)
            mEncryption.decrypt(Base64.decode(encodeBytes, Base64.NO_WRAP))
        } catch (ex: Throwable) {
            throw SecurityException("Could not decrypt key. " + ex.message, ex)
        }
    }

    override fun getStringSet(key: String?, defValues: MutableSet<String>?): MutableSet<String>? {
        val value = getDecryptedData(key)
        return if (value != null) (value as MutableSet<String>) else defValues
    }

    override fun getInt(key: String?, defValue: Int): Int {
        val value = getDecryptedData(key)
        return if (value != null && value is Int) value else defValue
    }

    override fun getLong(key: String?, defValue: Long): Long {
        val value = getDecryptedData(key)
        return if (value != null && value is Long) value else defValue
    }

    override fun getFloat(key: String?, defValue: Float): Float {
        val value = getDecryptedData(key)
        return if (value != null && value is Float) value else defValue
    }

    override fun getBoolean(key: String?, defValue: Boolean): Boolean {
        val value = getDecryptedData(key)
        return if (value != null && value is Boolean) value else defValue
    }

    override fun contains(key: String?): Boolean {
        return mSharedPreferences.contains(key)
    }

    override fun edit(): SharedPreferences.Editor {
        return this
    }

    override fun registerOnSharedPreferenceChangeListener(listener: SharedPreferences.OnSharedPreferenceChangeListener?) {
        mSharedPreferences.registerOnSharedPreferenceChangeListener(listener)
    }

    override fun unregisterOnSharedPreferenceChangeListener(listener: SharedPreferences.OnSharedPreferenceChangeListener?) {
        mSharedPreferences.unregisterOnSharedPreferenceChangeListener(listener)
    }

    override fun putString(key: String?, value: String?): SharedPreferences.Editor {

        //空安全
        val safeValue = value ?: NULL_VALUE
        //将值转为 byte[]
        val stringBytes = safeValue.toByteArray(UTF_8)
        //值转为byte[] 后的数据长度
        val stringByteLength = stringBytes.size
        //总的数据长度等于： 数据类型+数据长度+值转为byte[] 后的数据
        val buffer = ByteBuffer.allocate(Integer_BYTES + Integer_BYTES + stringByteLength)
        //数据类型
        buffer.putInt(DataType.STRING.type)
        //数据长度
        buffer.putInt(stringByteLength)
        //值转为byte[] 后的数据
        buffer.put(stringBytes)
        //加密过程
        putEncryptedData(key, buffer.array())
        return this
    }

    private fun putEncryptedData(key: String?, value: ByteArray) {

        //空安全
        val safeKey = key ?: NULL_VALUE
        try {
            val encryptedPair = encryptKeyValue(safeKey, value)
            //存储加密后的键和值
            mEditor.putString(encryptedPair.first, encryptedPair.second)
        } catch (ex: Throwable) {
            throw SecurityException("Could not encrypt data: " + ex.message, ex)
        }
    }

    //加密过程,将键和值都进行加密
    private fun encryptKeyValue(key: String, value: ByteArray): Pair<String, String> {

        val encryptKey = encryptString(key)
        val encryptValue = encryptBytes(value)
        return Pair(encryptKey, encryptValue)
    }

    private fun encryptString(key: String?): String {
        val safeKey = key ?: NULL_VALUE
        return encryptBytes(safeKey.toByteArray(UTF_8))
    }

    //最后都交给 IEncryption 的子类来实现实现加密过程
    private fun encryptBytes(bytes: ByteArray): String {

        return try {
            val encryptBytes = mEncryption.encrypt(bytes)
            val encodeBytes = Base64.encode(encryptBytes, Base64.NO_WRAP)
            String(encodeBytes, UTF_8)
        } catch (ex: Throwable) {
            throw SecurityException("Could not encrypt key. " + ex.message, ex)
        }
    }

    override fun putStringSet(key: String?, values: MutableSet<String>?): SharedPreferences.Editor {

        val arraySet = ArraySet<ByteArray>()
        //值转为byte[] 后的数据长度
        var length = 0
        if (null != values) {
            for (str: String in values) {
                val strByteArray = str.toByteArray(UTF_8)
                arraySet.add(strByteArray)
                length += (Integer_BYTES + strByteArray.size)
            }
        }
        //总的数据长度
        val buffer = ByteBuffer.allocate(Integer_BYTES + length)
        //数据类型
        buffer.putInt(DataType.STRING_SET.type)
        for (array: ByteArray in arraySet) {
            //数据长度
            buffer.putInt(array.size)
            //值转为byte[] 后的数据
            buffer.put(array)
        }
        //加密过程
        putEncryptedData(key, buffer.array())
        return this
    }

    override fun putInt(key: String?, value: Int): SharedPreferences.Editor {

        //总的数据长度等于： 数据类型+值转为byte[] 后的数据
        val buffer = ByteBuffer.allocate(Integer_BYTES + Integer_BYTES)
        //数据类型
        buffer.putInt(DataType.INT.type)
        //值转为byte[] 后的数据
        buffer.putInt(value)
        //加密过程
        putEncryptedData(key, buffer.array())
        return this
    }

    override fun putLong(key: String?, value: Long): SharedPreferences.Editor {
        //总的数据长度等于： 数据类型+值转为byte[] 后的数据
        val buffer = ByteBuffer.allocate(Integer_BYTES + Long_BYTES)
        //数据类型
        buffer.putInt(DataType.LONG.type)
        //值转为byte[] 后的数据
        buffer.putLong(value)
        //加密过程
        putEncryptedData(key, buffer.array())
        return this
    }

    override fun putFloat(key: String?, value: Float): SharedPreferences.Editor {
        //总的数据长度等于： 数据类型+值转为byte[] 后的数据
        val buffer = ByteBuffer.allocate(Integer_BYTES + Float_BYTES)
        //数据类型
        buffer.putInt(DataType.FLOAT.type)
        //值转为byte[] 后的数据
        buffer.putFloat(value)
        //加密过程
        putEncryptedData(key, buffer.array())
        return this
    }

    override fun putBoolean(key: String?, value: Boolean): SharedPreferences.Editor {
        //总的数据长度等于： 数据类型+值转为byte[] 后的数据
        val buffer = ByteBuffer.allocate(Integer_BYTES + Byte_BYTES)
        //数据类型
        buffer.putInt(DataType.BOOLEAN.type)
        //值转为byte[] 后的数据
        if (value) {
            buffer.put(1.toByte())
        } else {
            buffer.put(0.toByte())
        }
        //加密过程
        putEncryptedData(key, buffer.array())
        return this
    }

    override fun remove(key: String?): SharedPreferences.Editor {
        return mEditor!!.remove(key)
    }

    override fun clear(): SharedPreferences.Editor {
        return mEditor!!.clear()
    }

    override fun commit(): Boolean {
        return mEditor!!.commit()
    }

    override fun apply() {
        mEditor!!.apply()
    }

}